import json
import math
import numpy as np
from django.http import JsonResponse
from django.shortcuts import render
from django.views.generic import View

# 生成字母数字对应的表
dict = {}
for i in range(97, 123):
    dict[chr(i)] = i - 97
for i in range(65, 91):
    dict[chr(i)] = 26 + i - 65
for i in range(48, 58):
    dict[chr(i)] = 52 + i - 48


def getMatrix(self, matrix_string, row):  # 将矩阵串转换成矩阵
    matrix_list = matrix_string.split()
    matrix = []
    for i in range(0, len(matrix_list), row):
        row_list = matrix_list[i:i + row]
        # 将子列表中的数字字符串转换为整数并添加到加密矩阵中
        matrix.append([int(num) for num in row_list])
    matrix = np.array(matrix)
    print(matrix)
    return matrix


def getNumlist(self, string):  # 将字符串转换为数字
    num_list = []
    for i in string:
        num_list.append(dict[i])
    print('num', num_list)
    return num_list


def getString(self, num_list):  # 将数字串转为字符串
    result = ""
    for i in num_list:
        if i > 61:
            i = i % 62
        for k in dict:
            if dict[k] == i:
                result = result + k
    return result


def getInverse(self, matrix):
    step1 = np.linalg.det(matrix)
    step2 = round(((step1 % 62) + 62) % 62)  # 行列式的值
    inverse = 0
    # 求逆
    for i in range(1, 62):
        if (step2 * i) % 62 == 1:
            inverse = i
            break
    # 求伴随矩阵
    adj_matrix = np.linalg.inv(matrix) * np.linalg.det(matrix)
    # 将每个值对62取模
    adj_matrix = adj_matrix % 62
    inverse_matrix = np.dot(inverse, adj_matrix) % 62
    return inverse_matrix


def group(self, num_list, row, matrix):  # 分组加解密
    group_num = int(len(num_list) / row)
    whole__num_list = []
    for g in range(0, group_num):
        plaintext_matrix = np.array(num_list[0 + g * row: (g + 1) * row])
        encrypt_num_list = (np.matmul(matrix, plaintext_matrix))
        for num in encrypt_num_list:
            num = round(num)
            whole__num_list.append(num)
    return whole__num_list


def encrypt(self, row, string, matrix):  # 封装加密函数
    num_list = getNumlist(self, string)
    # 如果矩阵行数不能被明文整除，若不能则用'z'的数字25补全
    supple_num = len(num_list) % row
    if supple_num:
        for n in range(1, row - supple_num + 1):
            num_list.append(25)
    # 分组加密
    whole_encrypt_num_list = group(self, num_list, row, matrix)
    print(whole_encrypt_num_list)
    new = []
    for i in whole_encrypt_num_list:
        i = round(i)
        new.append(i)
    # 将加密后的数字转换为字母
    ciphertext = getString(self, new)
    print('whole_encrypt_num_list', new)
    return supple_num, ciphertext


def decrypt(self, row, string, matrix):  # 封装解密函数
    num_list = getNumlist(self, string)
    inverse_matrix = getInverse(self, matrix)
    # 分组解密
    whole_decrypt_num_list = group(self, num_list, row, inverse_matrix)
    print(whole_decrypt_num_list)
    # 将解密后的数字转换为对应字母
    plaintext = getString(self, whole_decrypt_num_list)
    print(plaintext)
    return plaintext


class HomeView(View):
    def get(self, request):
        return render(request, 'index.html')


class Encrypt(View):
    def post(self, request):
        string = request.POST.get('string')
        row = int(request.POST.get('row'))
        matrix_string = (request.POST.get('matrix'))

        matrix = getMatrix(self, matrix_string, row)
        flag = math.gcd(round(np.linalg.det(matrix)), 62)
        if flag == 1:
            supple_num, ciphertext = encrypt(self, row, string, matrix)
            params = {'ciphertext': ciphertext, 'supple_num': supple_num}
            return JsonResponse({'res': 1, 'message': 'success', 'params': params})
        else:
            return JsonResponse({'res': 2, 'message': '该矩阵不可逆，请重新输入'})


class Decrypt(View):
    def post(self, request):
        string = request.POST.get('string')
        row = int(request.POST.get('row'))
        matrix_string = (request.POST.get('matrix'))
        matrix = getMatrix(self, matrix_string, row)
        flag = math.gcd(round(np.linalg.det(matrix)), 62)
        if flag == 1:
            # 输入的明文转换为数字
            plaintext = decrypt(self, row, string, matrix)
            return JsonResponse({'res': 1, 'message': 'success', 'plaintext': plaintext})
        else:
            return JsonResponse({'res': 2, 'message': '该矩阵不可逆，请重新输入'})


class Crack(View):
    def get(self, request):
        return render(request, 'crack.html')

    def post(self, request):
        row = int(request.POST.get('row'))
        pairs = request.POST.get('pairs')
        data = json.loads(pairs)
        num_list1 = getNumlist(self, data[0]['plaintext'])
        num_list2 = getNumlist(self, data[0]['ciphertext'])
        # 将第一组明文转成矩阵格式
        p = []
        for i in range(0, len(num_list1), row):
            row_list = num_list1[i:i + row]
            # 将子列表中的数字字符串转换为整数并添加到加密矩阵中
            p.append([int(num) for num in row_list])
        p = np.array(p).T
        print(p)
        flag = math.gcd(round(np.linalg.det(p)), 62)

        if flag > 1:
            return JsonResponse({'res': 2, 'message': '明文矩阵不可逆，不可破译'})
        else:
            inverse_p = getInverse(self, p)
            print('inverse', inverse_p)
            # 将第一组密文转成矩阵格式
            c = []
            for i in range(0, len(num_list2), row):
                row_list = num_list2[i:i + row]
                c.append([int(num) for num in row_list])
            c = np.array(c).T
            k = (np.matmul(c, inverse_p) % 62)
            print('k', k)

            # 获取第二组明文进行验证
            string = data[1]['plaintext']
            num_list = getNumlist(self, string)
            # 分组加密
            supple, ciphertext = encrypt(self, row, string, k)
            print(ciphertext, data[1]['ciphertext'])
            # 判断加密矩阵是否正确
            if ciphertext == data[1]['ciphertext']:
                k = k.tolist()
                json_str = json.dumps(k)
                print(json_str)
                return JsonResponse({'res': 1, 'message': ciphertext, 'matrix': json_str})
            else:
                return JsonResponse({'res': 3, 'message': '破解失败'})
